package logic;

import java.io.Serializable;
import java.util.ArrayList;

import Server.Player;
import Ships.AircraftCarrier;
import Ships.Battleship;
import Ships.Cruiser;
import Ships.Destroyer;
import Ships.Ship;
import Ships.Submarine;



public class Board implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 6245199269603261957L;

	//vari�vel a ser returnada quando perder o jogo
	public final static int GAME_ENDED = 50;
	
	
	//valores possiveis de estar no tabuleiro 
	public final static int BOARD_EMPTY = 0;
	public final static int BOARD_SHIP = 1;
	public final static int BOARD_SHOT_IN_SHIP = 2;
	public final static int BOARD_SHOT_IN_WATER = 3;
	
	
	//tipos de resultado que um tiro pode ter
		
	
	public final static int SHIP_MISSED = 11;
	public final static int SHOT_OUTSIDE_PARAMETERS = 12;
	public final static int ALREADY_SHOT_THERE = 13;
	public final static int SHIP_HIT = 14;
	public final static int SHIP_HIT_WRECKED = 15;
	
	
	//para novos ships
	public final static int NEW_AIRCRAFT_CARRIER = 0;
	public final static int NEW_BATTLESHIP = 1;
	public final static int NEW_CRUISER = 2;
	public final static int NEW_DESTROYER = 3;
	public final static int NEW_SUBMARINE = 4;
	
	public final static int SHIP_NUMBER = 5;
	
	
	//tabuleiro de jogo e navios
	private int matrix [][];
	private int nLines;
	private int nColumns;
	private ArrayList<Ship> ships;
			
	public Board(int lines,int columns){
				
		this.nLines = lines;
		this.nColumns = columns;
		this.matrix = new int [lines][columns];
		
		this.ships = new ArrayList<Ship>();
		
		resetMatrix();
		
	}
	
	public int[][] getMatrix() {
		return matrix;
	}
	
	public void setMatrix(int[][] matrix) {
		this.matrix = matrix;
	}
	
	public ArrayList<Ship> getShips() {
		return ships;
	}
	
	public void setShips(ArrayList<Ship> ships) {
		this.ships = ships;
	}

	public int getNColumns() {
		return nColumns;
	}
	
	public int getNLines() {
		return nLines;
	}
	
	
	/*------------- Functions --------------*/
		
	
	/**
	 * Sets all the the matrix positions to zero (0)
	 */
	public void resetMatrix(){
		
		for(int l = 0; l<nLines ; l++)
			for(int c=0; c<nColumns; c++)
				matrix[l][c] = 0;

	}
	
	
	/**
	 * Funcao que coloca os "1's" nos locais da matriz onde existem barcos
	 */
	public void setShipPositionsInMatrix()
	{
			
		for(Ship ship : ships)
		{			
			if(ship.getOrientation() == Ship.HORIZONTAL)
			{
				for(int col=0; col<ship.getSize(); col++)
					matrix[ship.getLin_ini()][ship.getCol_ini() + col] = 1;
				
			}
			else if(ship.getOrientation() == Ship.VERTICAL)
			{
				for(int line=0; line<ship.getSize(); line++)
				{
					matrix[ship.getLin_ini() + line][ship.getCol_ini()] = 1;
				}
			}
		}
		
	}
	
	
	/**
	 * 
	 * @param shotLin
	 * @param shotCol
	 * @return
	 */
	/*
	public int verifyMatrix(int shotLin,int shotCol){
		
		boolean shipThere = false;
		
		if(matrix[shotLin][shotCol]==1)
		{
			for(int i=0;i<ships.size();i++)
			{
				shipThere = ships.get(i).verifyLocation(shotCol, shotLin);
				
				if(shipThere == true)
				{
					ships.get(i).setLife(ships.get(i).getLife()-1);
					
					if(ships.get(i).getLife()==0){
						wreckedShips.add(ships.get(i));
						if(wreckedShips.size()==ships.size()){
							return GAME_ENDED;
						}
					}
					
					return SHIP_HIT;
				}
			}
		}
		if(matrix[shotLin][shotCol]==2){
			return ALREADY_SHOT_THERE;
		}
		

		return SHIP_MISSED;
		
	}
	*/
	
	/**
	 * 
	 */
	public boolean isReadyToStart(){
		
		for (Ship ship : ships)
			if (ship.isWellPositioned(this) == false)
				return false;
		
		resetMatrix();
		setShipPositionsInMatrix();
		
		return true;
		
	}
	
	/**
	 * funcao que vai colocar a 3 os locais com agua onde se acertou e a 
	 * 4 o sitio onde se acertou num barco
	 * 
	 * @param shotLin
	 * @param shotCol
	 * @return
	 */
	
	/*
	public int matrixHit( int shotLin,int shotCol){
		
		if(shotLin > nLines || shotCol > nColumns || shotLin < 1 || shotCol < 1){
			return SHOT_OUTSIDE_PARAMETERS;
		}
		if(matrix[shotLin][shotCol]==0)
		{
			matrix[shotLin][shotCol]=3;
			return SHIP_MISSED;
		}
		if(matrix[shotLin][shotCol]==3 || matrix[shotLin][shotCol]==4)
		{
			return ALREADY_SHOT_THERE;
		}
		else{
			
			for(int i=0;i<ships.size();i++)
			{
				if(verifyMatrix( shotLin, shotCol)==1)
				{
					matrix[shotLin][shotCol]=4;
					return SHIP_HIT;
				}
			}

		
		}
		return 0;
	}
		*/
	
	/**
	 * 
	 * @param lin_ini
	 * @param col_ini
	 * @param orientation
	 * @param type
	 * @return
	 */
	
	/*
	public boolean addShip(int lin_ini,int col_ini, int orientation,int type){

		Ship new_ship = null;
		
		for(int i=0;i<ships.size();i++){
			if(ships.get(i).verifyLocation(col_ini, lin_ini)==true)
			{
				return false;
			}
		}

		switch(type){

		case NEW_AIRCRAFT_CARRIER:

			new_ship = new AircraftCarrier();
			new_ship.setCol_ini(col_ini);
			new_ship.setLin_ini(lin_ini);
			new_ship.setOrientation(orientation);
			if(new_ship.getSize()+lin_ini>nLines)
			{
				return false;
			}
			ships.add(new_ship);
			return true;

		case NEW_BATTLESHIP:

			new_ship = new Battleship();
			new_ship.setCol_ini(col_ini);
			new_ship.setLin_ini(lin_ini);
			new_ship.setOrientation(orientation);
			if(new_ship.getSize()+lin_ini>nLines)
			{
				return false;
			}
			ships.add(new_ship);
			return true;

		case NEW_CRUISER:

			new_ship = new Cruiser();
			new_ship.setCol_ini(col_ini);
			new_ship.setLin_ini(lin_ini);
			new_ship.setOrientation(orientation);
			if(new_ship.getSize()+lin_ini>nLines)
			{
				return false;
			}
			ships.add(new_ship);
			return true;

		case NEW_DESTROYER:

			new_ship = new Destroyer();
			new_ship.setCol_ini(col_ini);
			new_ship.setLin_ini(lin_ini);
			new_ship.setOrientation(orientation);
			if(new_ship.getSize()+lin_ini>nLines)
			{
				return false;
			}
			ships.add(new_ship);
			return true;

		case NEW_SUBMARINE:

			new_ship = new Submarine();
			new_ship.setCol_ini(col_ini);
			new_ship.setLin_ini(lin_ini);
			new_ship.setOrientation(orientation);
			if(new_ship.getSize()+lin_ini>nLines)
			{
				return false;
			}
			ships.add(new_ship);
			return true;


		default:
			return false;

		}



	}
	*/

	
/*	
	public int shoot(ArrayList <Ship> ships, int shotLin,int shotCol)
	{
		
		for(int i=0;i<ships.size();i++)
		{
			if()
			{
				ships.get(i);
				return SHIP_HIT;
			}
			
		}
		
		return SHIP_MISS;
	}*/
	
		
	
	/**
	 * 
	 * @param line
	 * @param column
	 * @return the state of the given position of the board.  (-1 if invalid position)
	 */
	public int getStateAt(int line, int column){
		
		if (line<0 || line >= nLines  || column<0 || column >= nColumns)
			return -1;
		else
			return matrix[line][column];
		
	}
	
	/**
	 * Shots at the given position of the Board matrix
	 * @param line
	 * @param column
	 * @return the result of the shot (SHIP_HIT, SHIP_MISSED, SHIP_HIT_WRECKED, ...)
	 */
	public int shotAt(int line, int column){

		switch(getStateAt(line, column)){
		
		case -1:
			return SHOT_OUTSIDE_PARAMETERS;
		
		case BOARD_SHIP:
			
			matrix[line][column] = BOARD_SHOT_IN_SHIP;
			Ship s = getShipAt(line, column);
			s.hit();
			
			if (s.isWrecked())
				return SHIP_HIT_WRECKED;
			else			
				return SHIP_HIT;
		
		case BOARD_EMPTY:
			matrix[line][column] = BOARD_SHOT_IN_WATER;
			return SHIP_MISSED;
			
		case BOARD_SHOT_IN_WATER:
		case BOARD_SHOT_IN_SHIP:
			return ALREADY_SHOT_THERE;
			
		}

		return -1;

	}

	
	/**
	 * 
	 * @param line
	 * @param column
	 * @return the Ship that is at the given position of the Board (if it is well positioned) or 
	 * NULL if no Ship is in that position
	 */
	public Ship getShipAt(int line, int column){
				
		for (Ship p: ships)
			if (p.isWellPositioned(this))
				if (p.getOrientation()==Ship.VERTICAL){
					if (line>= p.getLin_ini()&& line<(p.getLin_ini()+p.getSize()) && column==p.getCol_ini())
						return p;
				}
				else{
					if (column>= p.getCol_ini()&& column<(p.getCol_ini()+p.getSize()) && line==p.getLin_ini())
						return p;
				}

		
		return null;
	}

	
	/**
	 * 
	 * @param line
	 * @param column
	 * @return
	 */
	public int getNumShipsAt(int line, int column){
		
		int ct = 0;
		
		for (Ship p: ships)
			if (p.isAtBoard(this))
				if (p.getOrientation()==Ship.VERTICAL){
					if (line>= p.getLin_ini()&& line<(p.getLin_ini()+p.getSize()) && column==p.getCol_ini())
						ct++;
				}
				else{
					if (column>= p.getCol_ini()&& column<(p.getCol_ini()+p.getSize()) && line==p.getLin_ini())
						ct++;
				}

		
		return ct;
		
	}

	public ArrayList<Ship> getWreckedShips() {
		
		ArrayList<Ship> wShips = new ArrayList<Ship>();
		
		for (Ship s : ships)
			if (s.isWrecked())
				wShips.add(s);
		
			return wShips;
		
	}

	public boolean isAllShipsWrecked() {
		for (Ship s : ships)
			if (!s.isWrecked())
				return false;
		
		return true;
	}

	
	public static Board getDefaultBoard(){
		
		Board b = new Board(9, 9);
		
		b.ships = new ArrayList<Ship>();
		
		b.ships.add(new AircraftCarrier());
		b.ships.add(new Battleship());
		b.ships.add(new Cruiser());
		b.ships.add(new Destroyer());
		b.ships.add(new Submarine());
		
		return b;
	}
	
	public void setStateAt(int line, int column, int state){
		
		if (line>=0 && line<nLines)
			if (column>=0 && column<nColumns)
				matrix[line][column] = state;
		
	}
	
	public static Board getDefaultEnemyBoard(){
		Board b = new Board(9, 9);
		
		return b;
	}
	
}
